package com.airbnb.lottie.animation.content;

import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.TransformKeyframeAnimation;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.utils.MiscUtils;
import com.airbnb.lottie.LottieDrawable;
import com.airbnb.lottie.model.content.Repeater;
import com.airbnb.lottie.model.layer.BaseLayer;
import com.airbnb.lottie.value.LottieValueCallback;

import ohos.agp.render.Canvas;
import ohos.agp.render.Path;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

public class RepeaterContent implements DrawingContent,PathContent,GreedyContent,
        BaseKeyframeAnimation.AnimationListener,KeyPathElementContent {
				
    private final Matrix matrix = new Matrix();
    private final Path path = new Path();

    private final LottieDrawable lottieDrawable;
    private final BaseLayer layer;
    private final String name;
    private final boolean hidden;
    private final BaseKeyframeAnimation<Float, Float> copies;
    private final BaseKeyframeAnimation<Float, Float> offset;
    private final TransformKeyframeAnimation transform;
    private ContentGroup contentGroup;

    public RepeaterContent(LottieDrawable lottieDrawable, BaseLayer layer, Repeater repeater) {
        this.lottieDrawable = lottieDrawable;
        this.layer = layer;
        name = repeater.getName();
        this.hidden = repeater.isHidden();
        copies = repeater.getCopies().createAnimation();
        layer.addAnimation(copies);
        copies.addUpdateListener(this);

        offset = repeater.getOffset().createAnimation();
        layer.addAnimation(offset);
        offset.addUpdateListener(this);

        transform = repeater.getTransform().createAnimation();
        transform.addAnimationsToLayer(layer);
        transform.addListener(this);
    }

    @Override
    public void absorbContent(ListIterator<Content> contentsIter) {
        // This check prevents a repeater from getting added twice.
        // This can happen in the following situation:
        //    RECTANGLE
        //    REPEATER 1
        //    FILL
        //    REPEATER 2
        // In this case, the expected structure would be:
        //     REPEATER 2
        //        REPEATER 1
        //            RECTANGLE
        //        FILL
        // Without this check, REPEATER 1 will try and absorb contents once it is already inside of
        // REPEATER 2.
        if (contentGroup != null) {
            return;
        }
        // Fast forward the iterator until after this content.
        while (contentsIter.hasPrevious() && contentsIter.previous() != this) {}
        List<Content> contents = new ArrayList<>();
        while (contentsIter.hasPrevious()) {
            contents.add(contentsIter.previous());
            contentsIter.remove();
        }
        Collections.reverse(contents);
        contentGroup = new ContentGroup(lottieDrawable, layer, "Repeater", hidden, contents, null);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
        contentGroup.setContents(contentsBefore, contentsAfter);
    }

    @Override
    public Path getPath() {
        Path contentPath = contentGroup.getPath();
        path.reset();
        float copies = this.copies.getValue();
        float offset = this.offset.getValue();
        for (int i = (int) copies - 1; i >= 0; i--) {
            matrix.setMatrix(transform.getMatrixForRepeater(i + offset));
            path.addPath(contentPath,matrix,Path.AddPathMode.EXTEND_ADD_PATH_MODE);
        }
        return path;
    }

    @Override
    public void draw(Canvas canvas, Matrix parentMatrix, float alpha) {
        float copies = this.copies.getValue();
        float offset = this.offset.getValue();
        float startOpacity = this.transform.getStartOpacity().getValue() / 100f;
        float endOpacity = this.transform.getEndOpacity().getValue() / 100f;
        for (int i = (int) copies - 1; i >= 0; i--) {
            matrix.setMatrix(parentMatrix);
            matrix.preConcat(transform.getMatrixForRepeater(i + offset));
            float newAlpha = alpha * MiscUtils.lerp(startOpacity, endOpacity, i / copies);
            contentGroup.draw(canvas, matrix, (int) newAlpha);
        }
    }

    @Override
    public void getBounds(RectFloat outBounds, Matrix parentMatrix, boolean applyParents) {
        contentGroup.getBounds(outBounds, parentMatrix, applyParents);
    }

    @Override
    public void onValueChanged() {
        lottieDrawable.invalidateSelf();
    }

    @Override
    public void resolveKeyPath(KeyPath keyPath, int depth, List<KeyPath> accumulator, KeyPath currentPartialKeyPath) {
        MiscUtils.resolveKeyPath(keyPath, depth, accumulator, currentPartialKeyPath, this);
    }

    @Override
    public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> callback) {
        if (transform.applyValueCallback(property, callback)) {
            return;
        }

        if (property == LottieProperty.REPEATER_COPIES) {
            copies.setValueCallback((LottieValueCallback<Float>) callback);
        } else if (property == LottieProperty.REPEATER_OFFSET) {
            offset.setValueCallback((LottieValueCallback<Float>) callback);
        }
    }
}
